package com.me.study.javaCore.Bytecode.byteBuddy;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

/**
 * 字节码增强框架（ByteBuddy）测试
 */
public class ByteBuddyUnitTest {

    @Test
    public void givenObject_whenToString_thenReturnHelloWorldString() throws InstantiationException, IllegalAccessException {
        DynamicType.Unloaded<?> unloadedType = new ByteBuddy()
                .subclass(Object.class)// 继承Object类
                .method(ElementMatchers.isToString())// 匹配 toString方法
                .intercept(FixedValue.value("Hello World ByteBuddy!"))// 提供 toString()的实现
                .make();// 生成一个新的类

        // 加载类
        Class<?> dynamicType = unloadedType.load(getClass().getClassLoader()).getLoaded();

        assertEquals(dynamicType.newInstance().toString(), "Hello World ByteBuddy!");
    }

    @Test
    public void givenFoo_whenRedefined_thenReturnFooRedefined() throws Exception {
        ByteBuddyAgent.install();
        DynamicType.Unloaded<?> unloadedType = new ByteBuddy().redefine(Foo.class)// 重新定义 Foo
                .method(ElementMatchers.named("sayHelloFoo"))// 匹配 sayHelloFoo()
                .intercept(FixedValue.value("Hello Foo Redefined"))// 提供 方法实现
                .make();// 创建动态类

        // 覆盖已存在的类，可以直接用
        unloadedType.load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
        Foo f = new Foo();
        assertEquals(f.sayHelloFoo(), "Hello Foo Redefined");
    }

    @Test
    public void givenSayHelloFoo_whenMethodDelegation_thenSayHelloBar() throws IllegalAccessException, InstantiationException {
        String r = new ByteBuddy().subclass(Foo.class)
                // 匹配 Foo类中 返回类型是 String 的 sayHelloFoo()
                .method(ElementMatchers.named("sayHelloFoo")
                        .and(ElementMatchers.isDeclaredBy(Foo.class)
                        .and(ElementMatchers.returns(String.class))))
                .intercept(MethodDelegation.to(Bar.class))
                .make()
                .load(getClass().getClassLoader())
                .getLoaded()
                .newInstance()
                .sayHelloFoo();

        assertEquals(r, Bar.sayHelloBar());
    }

    @Test
    public void givenMethodName_whenDefineMethod_thenCreateMethod() throws Exception {
        Class<?> type = new ByteBuddy().subclass(Object.class).name("MyClassName").defineMethod("custom", String.class, Modifier.PUBLIC).intercept(MethodDelegation.to(Bar.class)).defineField("x", String.class, Modifier.PUBLIC).make()
                .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();

        Method m = type.getDeclaredMethod("custom");

        assertEquals(m.invoke(type.newInstance()), Bar.sayHelloBar());
        assertNotNull(type.getDeclaredField("x"));
    }

}
